/*
 * uart.c
 *
 * Created: 2013-11-5 7:32:42
 *  Author: alivehex
 */
#include "uart.h"
#include "gpio.h"
#include "board.h"
#include <stdarg.h>
#include <stdio.h>

int uart1_open(unsigned long baudrate) {
	PORTD |= (BOARD_DBG_TXD | BOARD_DBG_RXD);
	DDRD |= BOARD_DBG_TXD; // output
	DDRD &= ~BOARD_DBG_RXD; // input
	
	// baudrate and frame length etc.
	UBRR0L= (BOARD_MCK / baudrate / 8 - 1) % 256;
	UBRR0H= (BOARD_MCK / baudrate / 8 - 1) / 256;
	
	// 8bits and 1stop
	UCSR0C = (3 << 1);
	
	// double the rate
	UCSR0A = (1 << 1);
	
	// enable tx and rx and interrupt
	UCSR0B = (1 << 4) | (1 << 3);
#ifndef ENABLE_USB
	UCSR0B |= (1 << 7); // | (1 << 6);
#endif
	return 0;
}

int uart1_open2(uint16_t ubrr, int dataBits, int stopBits, int parity) {
	uint8_t buffer = 0;
	
	PORTD |= (BOARD_DBG_TXD | BOARD_DBG_RXD);
	DDRD |= BOARD_DBG_TXD; // output
	DDRD &= ~BOARD_DBG_RXD; // input
	
	// baudrate and frame length etc.
	// UBRR0L= (BOARD_MCK / baudrate / 8 - 1) % 256;
	// UBRR0H= (BOARD_MCK / baudrate / 8 - 1) / 256;
	UBRR0L = ubrr & 0xFF;
	UBRR0H = (ubrr >> 8) & 0xFF;
	
	// data bits and stop bits
	switch(dataBits) {
	case 5:
		buffer |= (0 << 1);
		break;
	case 6:
		buffer |= (1 << 1);
		break;
	case 7:
		buffer |= (2 << 1);
		break;
	case 8:
		buffer |= (3 << 1);
		break;
	case 9:
		buffer |= (7 << 1);
		break;
	default:
		return -1;
	}
	switch(stopBits) {
	case 1:
		buffer |= (0 << 3);
		break;
	case 2:
		buffer |= (1 << 3);
		break;
	default:
		return -1;
	}
	switch(parity) {
	case 0:
		buffer |= (0 << 4);
		break;
	case 1:
		buffer |= (3 << 4);
		break;
	case 2:
		buffer |= (2 << 4);
		break;
	default:
		return -1;
	}
	
	// UCSR0C = (3 << 1);
	UCSR0C = buffer;
	
	// double the rate
	UCSR0A = (1 << 1);
	
	// enable tx and rx and interrupt
	UCSR0B = (1 << 4) | (1 << 3);
#ifndef ENABLE_USB
	UCSR0B |= (1 << 7); // | (1 << 6);
#endif
	return 0;
}

#if (0)
int uartOpen(unsigned long bdr, int dataBits, int stopBits, int parity) {
	uint8_t divLsb, divMsb;
	
	// cli();
	// disable the interrupt
	UCSR0B &= ~((1 << 6) | (1 << 7));
	// wait for end of TXE
	if( (UCSR0A & (1 << 5)) == 0) {
		while ( (UCSR0A & (1 << 6)) == 0);
		// clear the interrupt
		UCSR0A |= (1 << 6);
	}
	// read any data in the buffer
	if(UCSR0A & (1 << 7)) {
		divLsb = UDR0;
		divLsb = UDR0;
	}
	// disable TX and RX
	UCSR0B &= ~((1 << 4) | (1 << 3));
	
	// config the IO 
	_CONFIG_IO_OUTPUT(BOARD_DBG_PORT, BOARD_DBG_TXD);
	_CONFIG_IO_INPUT_PULLUP(BOARD_DBG_PORT, BOARD_DBG_RXD);
	
	divLsb = 0;
	divMsb = 0;
	switch(bdr) {
	case 2400:	  divLsb = 64; divMsb = 3; break;
	case 4800:	  divLsb = 160; divMsb = 1; break;
	case 9600:	  divLsb = 207; break;
	case 14400:	  divLsb = 138; break;
	case 19200:	  divLsb = 103; break;
	case 28800:	  divLsb = 68; break;//EEOR FROM HERE
	case 38400:	  divLsb = 51; break;//OK
	case 57600:	  divLsb = 34; break;//OK
	case 76800:	  divLsb = 25; break;//OK
	case 115200:  divLsb = 16; break;//OK
	case 230400:  divLsb = 8; break; //OK
	case 250000:  divLsb = 7; break; //OK
	case 500000:  divLsb = 3; break; //OK
	case 1000000: divLsb = 1; break; //OK
	default:
		divLsb = (F_CPU / bdr / 8 - 1) % 256;
		divMsb = (F_CPU / bdr / 8 - 1) / 256;
		break;
	}
	UBRR0L = divLsb;
	UBRR0H = divMsb;
	
	// data bits
	uartSetDataBit(dataBits);
	// stop bits
	uartSetStopBit(stopBits);
	// parity
	uartSetParity(parity);
	// double the rate
	UCSR0A |= (1 << 1);
	
	// enable TX, RX and interrupt
	UCSR0B |= (1 << 4) | (1 << 3);
#ifndef ENABLE_USB
	UCSR0B |= (1 << 7); // | (1 << 6);
#endif
	return 0;
}

void uartSetBaudrate(unsigned long baudrate) {
	// wait for end of TX
	UBRR0L= (BOARD_MCK / baudrate / 8 - 1) % 256;
	UBRR0H= (BOARD_MCK / baudrate / 8 - 1) / 256;
}

/* len = 5, 6, 7, 8, 9*/
void uartSetDataBit(int len) {
	UCSR0C &= ~(3 << 1);
	UCSR0B &= ~(1 << 2);
	switch(len) {
		case 5:
		break;
		case 6:
		UCSR0C |= (1 << 1);
		break;
		case 7:
		UCSR0C |= (2 << 1);
		break;
		case 8:
		UCSR0C |= (3 << 1);
		break;
		case 9:
		UCSR0C |= (3 << 1);
		UCSR0B |= (1 << 2);
		break;
	}
}

/* len = 1, 2 */
void uartSetStopBit(int len) {
	UCSR0C &= ~(1 << 3);
	if(len == 2) {
		UCSR0C |= (1 << 3);
	}
}

/* parity = 0(None), 1(Even), 2(Odd) */
void uartSetParity(int parity) {
	UCSR0C &= ~(3 << 4);
	switch(parity) {
	case 0:
		break;
	case 1:
		UCSR0C |= (3 << 4);
		break;
	case 2:
		UCSR0C |= (2 << 4);
		break;
	}
}
#endif

int uart1_close(void) {
	PORTD &= ~(BOARD_DBG_TXD | BOARD_DBG_RXD);
	
	// disable tx and rx
	UCSR0B &= ~(1 << 3) | (1 << 4 );
	return 0;
}

int uart1_putc(char c) {
	while ( ( UCSR0A & (1 << 5)) == 0);
	UDR0 = c;
	return c;
}

int uart1_getc(int timeout) {
	if(timeout > 0) {
		while ( !(UCSR0A & (1 << 7)) && (-- timeout));
		if(timeout == 0) {
			return -1;
			} else {
			return UDR0;
		}
	} else {
		while ( !(UCSR0A & (1 << 7)) );
		return UDR0;
	}
}

int uart1_puts(char *c) {
	while (*c != 0) {
		uart1_putc(*c ++);
	}
	return 0;
}

void uart1_wait() {
	while ( !( UCSR0A & (1 << 5)) );
}

#if (1)
int kprintf(const char *str, ...) {
	char string[32];
	va_list ap;

	va_start(ap,str);
	vsprintf(string,str,ap);
	uart1_puts(string);
	va_end(ap);
	return 0;
}
#else
int kprintf(const char *str, ...) {
	while(0);
}	
#endif
